//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_TYPE_LIST_H
#define _UXS_TYPE_LIST_H

#pragma once

#include "TemplateUtils.h"
#include "TTypeInfo.h"

namespace UX_STL
{
namespace CompileTime
{
	using namespace UXTypes;


	//
	// Type List
	//

	template <typename Left, typename Right>
	struct TypeList;


	namespace _ctime_hidden_
	{
		//
		// Type List Push Back
		//
		
		template <typename Typelist, typename Type>
		struct TTypeList_PushBack;
 
		template <typename Left, typename Right, typename Type>
		struct TTypeList_PushBack < TypeList< Left, Right >, Type >
		{
			typedef TypeList< Left, typename TTypeList_PushBack< Right, Type >::type >	type;
		};

		template <typename Right, typename Type>
		struct TTypeList_PushBack < TypeList< void, Right >, Type >
		{
			typedef TypeList< Type, void >	type;
		};

		template <typename Type>
		struct TTypeList_PushBack< void, Type >
		{
			typedef TypeList< Type, void >	type;
		};



		//
		// Type List Push Front
		//
		
		template <typename Typelist, typename Type>
		struct TTypeList_PushFront
		{
			typedef TypeList< Type, Typelist >	type;
		};



		//
		// Type List Length
		//

		template <typename Typelist>
		struct TTypeList_Length;
 
		template <typename Left, typename Right>
		struct TTypeList_Length < TypeList< Left, Right > >
		{
			static const usize	value = 1 + TTypeList_Length< Right >::value;
		};

		template <typename Right>
		struct TTypeList_Length < TypeList< void, Right > >
		{
			static const usize	value = 0;
		};

		template <>
		struct TTypeList_Length< void >
		{
			static const usize	value = 0;
		};



		//
		// Type List Get Element
		//

		template <typename Typelist, usize Index>
		struct TTypeList_GetElem;

		template <typename Typelist>
		struct TTypeList_GetElem < Typelist, 0 >
		{
			typedef Typelist	type;
		};

		template <typename Left, typename Right>
		struct TTypeList_GetElem < TypeList< Left, Right >, 0 >
		{
			typedef Left		type;
		};

		template <typename Left, typename Right, usize Index>
		struct TTypeList_GetElem < TypeList< Left, Right >, Index >
		{
			typedef typename TTypeList_GetElem< Right, Index-1 >::type	type;
		};

		template <typename Right, usize Index>
		struct TTypeList_GetElem < TypeList< void, Right >, Index >
		{
			typedef void	type;
		};

		template <usize Index>
		struct TTypeList_GetElem < void, Index >
		{
			typedef void	type;
		};


		//
		// Type List Static For Each
		//
		/*
			example:
			struct Func
			{
				template <usize L, usize R>
				struct Op { static const usize	value = L + R; };

				template <typename T, usize Index>
				struct Process { static const usize	value = sizeof(T); };

				static const usize	init_value = 0;
			};
		*/

		template <typename Typelist, typename Func, typename ValueType, usize Index>
		struct TTypeList_StaticForEach;

		template <typename Left, typename Right, typename Func, typename ValueType, usize Index>
		struct TTypeList_StaticForEach< TypeList< Left, Right >, Func, ValueType, Index >
		{
			typedef typename Func::template Process< Left, Index >	_process_t;

			static const ValueType	left  = _process_t::value;
			static const ValueType	right = TTypeList_StaticForEach< Right, Func, ValueType, Index+1 >::value;

			typedef typename Func::template Op< left, right >	_op_t;

			static const ValueType	value = _op_t::value;
		};
			  
		template <typename Right, typename Func, typename ValueType, usize Index>
		struct TTypeList_StaticForEach< TypeList< void, Right >, Func, ValueType, Index >
		{
			static const ValueType	value = Func::init_value;
		};

		template <typename Func, typename ValueType, usize Index>
		struct TTypeList_StaticForEach< void, Func, ValueType, Index >
		{
			static const ValueType	value = Func::init_value;
		};



		//
		// Type List Type Static For Each
		//
		/*
			example:
			struct Func
			{
				template <typename L, typename R>
				struct Op { typedef TypeList< L, R >	type; };

				template <typename T, usize Index>
				struct Process { typedef T	type; };

				typedef void	init_type;
			};
		*/

		template <typename Typelist, typename Func, usize Index>
		struct TTypeList_TypeStaticForEach;

		template <typename Left, typename Right, typename Func, usize Index>
		struct TTypeList_TypeStaticForEach< TypeList< Left, Right >, Func, Index >
		{
			typedef typename Func::template Process< Left, Index >::type				left;
			typedef typename TTypeList_TypeStaticForEach< Right, Func, Index+1 >::type	right;
			typedef typename Func::template Op< left, right >::type						type;
		};
			  
		template <typename Right, typename Func, usize Index>
		struct TTypeList_TypeStaticForEach< TypeList< void, Right >, Func, Index >
		{
			typedef typename Func::init_type		type;
		};

		template <typename Func, usize Index>
		struct TTypeList_TypeStaticForEach< void, Func, Index >
		{
			typedef typename Func::init_type		type;
		};



		//
		// Type List For Each
		//
		/*
			example:
			struct Func
			{
				usize result;

				template <typename T, usize Index>
				void Process ()	{ result += sizeof(T); }
			};
		*/

		template <typename Typelist, typename Func, usize Index>
		struct TTypeList_ForEach;
		
		template <typename Left, typename Right, typename Func, usize Index>
		struct TTypeList_ForEach< TypeList< Left, Right >, Func, Index >
		{
			static void Iterate (Func &func)
			{
				func.template Process< Left, Index >();
				TTypeList_ForEach< Right, Func, Index+1 >::Iterate( func );
			}
		};

		template <typename Right, typename Func, usize Index>
		struct TTypeList_ForEach< TypeList< void, Right >, Func, Index > {
			static void Iterate (Func &func)
			{}
		};

		template <typename Func, usize Index>
		struct TTypeList_ForEach< void, Func, Index > {
			static void Iterate (Func &func)
			{}
		};

	
		//
		// Helpers
		//
		
		struct TTypeList_ToStringFunc
		{
			const uni_c_string	divider;
			string				result;

			explicit
			TTypeList_ToStringFunc (uni_c_string divider) : divider(divider)
			{}

			template <typename T, usize Index>
			void Process ()
			{
				result << '[' << Index << "] " << TypeInfo<T>::Name() << divider;
			}
		};
		
		template <typename Type>
		struct TTypeList_GetFirstIndexOfType
		{
			static const usize	init_value = -1;

			template <usize L, usize R>
			struct Op { static const usize	value = ( L >= 0 and R >= 0 ? MinMax< usize, L, R >::min : MinMax< usize, L, R >::max ); };

			template <typename T, usize Index>
			struct Process { static const usize	value = TypeDescriptor::IsSameTypes< T, Type >::value ? Index : init_value; };
		};
		
		template <typename Type>
		struct TTypeList_GetLastIndexOfType
		{
			static const usize	init_value = -1;

			template <usize L, usize R>
			struct Op { static const usize	value = MinMax< usize, L, R >::max; };

			template <typename T, usize Index>
			struct Process { static const usize	value = TypeDescriptor::IsSameTypes< T, Type >::value ? Index : init_value; };
		};
		
		template <typename TTypeList>
		struct TTypeList_Equal
		{
			static const bool	init_value = true;

			template <bool L, bool R>
			struct Op { static const bool	value = (L and R); };

			template <typename T, usize Index>
			struct Process { static const bool	value = TypeDescriptor::IsSameTypes< T, typename TTypeList::Get< Index >::type >::value; };
		};
		
		struct TTypeList_MaxSize
		{
			template <usize L, usize R>
			struct Op { static const usize	value = CompileTime::MinMax< usize, L, R >::max; };

			template <typename T, usize Index>
			struct Process { static const usize	value = sizeof(T); };

			static const usize	init_value = 0;
		};

		template <usize First, usize Count>
		struct TTypeList_SubList
		{
			static const usize	Last = First + Count;

			template <typename L, typename R>
			struct Op {
				typedef typename SwitchType< TypeDescriptor::IsVoid<L>::value, R, TypeList< L, R > >::type	type;
			};

			template <typename T, usize Index>
			struct Process {
				typedef typename SwitchType< (Index >= First and Index < Last), T, void >::type		type;
			};

			typedef void	init_type;
		};

	}	// _ctime_hidden_
	
	

	//
	// Type List
	//

	template <typename Left, typename Right = void>
	struct TypeList
	{
	// types
	public:
		typedef TypeList< Left, Right >		Self;

		typedef Left	left_t;
		typedef Right	right_t;

		typedef bool	_is_type_list;


	// static values
	public:
		struct Length {
			static const usize		value = _ctime_hidden_::TTypeList_Length< Self >::value;
		};

		template <typename Func, typename ValueType = usize>
		struct StaticForEach {
			static const ValueType	value = _ctime_hidden_::TTypeList_StaticForEach< Self, Func, ValueType, 0 >::value;
		};
		
		template <typename Type>
		struct FirstIndexOf {
			static const usize		value = StaticForEach< _ctime_hidden_::TTypeList_GetFirstIndexOfType< Type >, usize >::value;
		};
		
		template <typename Type>
		struct IndexOf {
			static const usize		value = FirstIndexOf< Type >::value;
		};
		
		template <typename Type>
		struct LastIndexOf {
			static const usize		value = StaticForEach< _ctime_hidden_::TTypeList_GetLastIndexOfType< Type >, usize >::value;
		};

		template <typename Type>
		struct HasType {
			static const bool		value = ( IndexOf< Type >::value != -1 );
		};

		template <typename TTypeList>
		struct Equal {
			static const bool		value = Length::value == TTypeList::Length::value and
											StaticForEach< _ctime_hidden_::TTypeList_Equal< TTypeList >, bool >::value;
		};

		struct MaxSizeOf {
			static const usize		value = StaticForEach< _ctime_hidden_::TTypeList_MaxSize, usize >::value;
		};

		template <usize Index, typename Type>
		struct IsType {
			static const usize		value = TypeDescriptor::IsSameTypes< typename Get< Index >::type, Type >::value;
		};
		
		template <typename Func>
		struct TypeForEach {
			typedef typename _ctime_hidden_::TTypeList_TypeStaticForEach< Self, Func, 0 >::type	type;
		};

		template <usize Index>
		struct Get {
			STATIC_ASSERTE( Index < Length::value );
			typedef typename _ctime_hidden_::TTypeList_GetElem< Self, Index >::type		type;
		};

		struct Front {
			typedef typename Get<0>::type	type;
		};

		struct Back {
			typedef typename Get< Length::value-1 >::type	type;
		};

		template <typename Type>
		struct PushBack {
			typedef typename _ctime_hidden_::TTypeList_PushBack< Self, Type >::type		type;
		};

		template <typename Type>
		struct PushFront {
			typedef typename _ctime_hidden_::TTypeList_PushFront< Self, Type >::type	type;
		};

		template <usize First, usize Count>
		struct SubList {
			STATIC_ASSERTE( First < Length::value and First + Count <= Length::value );
			typedef typename TypeForEach< _ctime_hidden_::TTypeList_SubList< First, Count > >::type	type;
		};

		struct PopBack {
			STATIC_ASSERTE( Length::value > 1 );
			typedef typename SubList< 0, Length::value-1 >::type	type;
		};

		struct PopFront {
			STATIC_ASSERTE( Length::value > 1 );
			typedef typename SubList< 1, Length::value-1 >::type	type;
		};


	// methods
	public:
		template <typename Func>
		static void ForEach (Func &func)
		{
			_ctime_hidden_::TTypeList_ForEach< Self, Func, 0 >::Iterate( func );
		}

		static string ToString (uni_c_string divider = "\n")
		{
			_ctime_hidden_::TTypeList_ToStringFunc func( divider );

			ForEach( func );

			if ( not func.result.Empty() )
				func.result.EraseFromBack( divider.Length() );

			return func.result;
		}
	};



	template <typename T0, typename T1 = void, typename T2 = void, typename T3 = void>
	struct TypeList4
	{
		typedef TypeList< T0, TypeList< T1, TypeList< T2, T3 > > >	type;
	};

	
	template <typename T0,		  typename T1 = void, typename T2 = void, typename T3 = void,
			  typename T4 = void, typename T5 = void, typename T6 = void, typename T7 = void,
			  typename T8 = void, typename T9 = void>
	struct TypeList10
	{
		typedef	typename TypeList4< T0, T1, T2,
					typename TypeList4< T3, T4, T5,
						typename TypeList4< T6, T7, T8, T9 >::type
					>::type
				>::type			type;
	};

	
	template <typename T00,		   typename T01 = void, typename T02 = void, typename T03 = void,
			  typename T04 = void, typename T05 = void, typename T06 = void, typename T07 = void,
			  typename T08 = void, typename T09 = void,
			  typename T10 = void, typename T11 = void, typename T12 = void, typename T13 = void,
			  typename T14 = void, typename T15 = void, typename T16 = void, typename T17 = void,
			  typename T18 = void, typename T19 = void
			 >
	struct TypeList20
	{
		typedef	typename TypeList4< T00, T01, T02,
					typename TypeList4< T03, T04, T05,
						typename TypeList4< T06, T07, T08,
							typename TypeList4< T09, T10, T11,
								typename TypeList4< T12, T13, T14,
									typename TypeList4< T15, T16, T17,
										typename TypeList4< T18, T19 >::type
									>::type
								>::type
							>::type
						>::type
					>::type
				>::type			type;
	};


//-------------------------------------------------------------------

}	// CompileTime
}	// UX_STL

#endif	// _UXS_TYPE_LIST_H